﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using NHibernate;
using ProgressTracker.Data.Abstract;
using ProgressTracker.Data.Concrete;
using ProgressTracker.Data.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ProgressTracker.Tests.Domain.Concrete
{
    [TestClass]
    public class DayRepositoryTests
    {
        private Mock<ISession> _session;
        private Mock<ITransaction> _transaction;
        private Mock<ITaskRepository> _taskRepo;
        private Mock<IUserRepository> _userRepo;
        private Task _testTask;

        private const string _testUserEmail = "fake@fake.com";

        private DateTime _testDate;

        [TestInitialize]
        public void Initialize()
        {
            _session = new Mock<ISession>();
            _transaction = new Mock<ITransaction>();
            _taskRepo = new Mock<ITaskRepository>();
            _testTask = new Task
                {
                    Id = 1,
                    Name = "Task",
                    Description = "Description"
                };

            _testDate = new DateTime(2001, 01, 19);

            _transaction.Setup(x => x.Commit()).Verifiable();
            _session.Setup(x => x.BeginTransaction()).Returns(_transaction.Object);
            _taskRepo.Setup(x => x.GetCurrentTasks(It.IsAny<int>())).Returns(new List<Task>
            {
                _testTask
            });

            _userRepo = new Mock<IUserRepository>();
            _userRepo.Setup(x => x.GetUserByEmail(It.IsAny<string>())).Returns(new User());
        }

        /*
         * GetDay currently contains un-mockable code (QueryOver). Since the call is just a strict
         * query/return, I'll omit testing it for now.
         */

        [TestMethod]
        public void Save_Calls_SaveOrUpdate()
        {
            var testDay = new Day
            {
                Date = _testDate
            };

            _session.Setup(x => x.SaveOrUpdate(testDay)).Verifiable();

            var dayRepo = new DayRepository(_session.Object, _taskRepo.Object, _userRepo.Object);
            dayRepo.Save(testDay);

            _session.Verify();
        }

        [TestMethod]
        public void Save_Commits_Transaction()
        {
            var testDay = new Day
            {
                Date = _testDate
            };

            var dayRepo = new DayRepository(_session.Object, _taskRepo.Object, _userRepo.Object);
            dayRepo.Save(testDay);

            _transaction.Verify();
        }

        [TestMethod]
        public void CreateNewDay_Gets_Correct_User()
        {
            _userRepo.Setup(x => x.GetUserByEmail(_testUserEmail))
                .Returns(new User()).Verifiable();

            var dayRepo = new DayRepository(_session.Object, _taskRepo.Object, _userRepo.Object);
            var day = dayRepo.CreateNewDay(_testDate, _testUserEmail);

            _userRepo.Verify();
        }

        [TestMethod]
        public void CreateNewDay_Assigns_Correct_Tasks_To_Day()
        {
            var dayRepo = new DayRepository(_session.Object, _taskRepo.Object, _userRepo.Object);
            var day = dayRepo.CreateNewDay(_testDate, _testUserEmail);

            Assert.IsTrue(day.Tasks.Contains(_testTask));
        }

        [TestMethod]
        public void CreateNewDay_Assigns_Correct_Date()
        {
            var dayRepo = new DayRepository(_session.Object, _taskRepo.Object, _userRepo.Object);
            var day = dayRepo.CreateNewDay(_testDate, _testUserEmail);

            Assert.AreEqual(_testDate, day.Date);
        }

        [TestMethod]
        public void CreateNewDay_Saves_Day()
        {
            _session.Setup(x => x.SaveOrUpdate(It.IsAny<Day>())).Verifiable();

            var dayRepo = new DayRepository(_session.Object, _taskRepo.Object, _userRepo.Object);
            var day = dayRepo.CreateNewDay(_testDate, _testUserEmail);

            _session.Verify();
        }

        [TestMethod]
        public void AddTaskToDay_Correctly_Adds_Task_To_Day()
        {
            var testDay = new Day
            {
                Date = _testDate
            };

            _session.Setup(x => x.SaveOrUpdate(It.Is<Day>(
                d => d.Tasks.Contains(_testTask)))).Verifiable();

            var dayRepo = new DayRepository(_session.Object, _taskRepo.Object, _userRepo.Object);
            dayRepo.AddTaskToDay(testDay, _testTask);

            _session.Verify();
        }
    }
}
